# _*_conding:cp-1252-*-
# 教育机构：银杰教育
# 讲师：李伟
# 开发时间：2025/8/30 下午9:26
"""
代码执行顺序从上到下依次执行，一旦某行代码报错了，后面代码终止运行。
- 不希望某一行出错了，后面的代码整个都不能继续运行了；
- 希望结果： 这行代码报错，捕获这个错误做一些相关的操作【记录日志，做一些错误提示，警告等】，后面的代码依然正常运行。
  - 使用异常捕获功能。

代码报错的类型：
1、NameError: name 'a' is not defined  名称错误：名称 'a' 未定义
2、IndexError: list index out of range 索引错误：列表索引超出范围
3、KeyError:                          'age' 键错误：'age'   这个错误通常发生在使用字典时，你尝试访问一个不存在的键（在这个例子中是 'age'
4、ValueError: invalid literal for int() with base 10: 'b'  值错误：用于int()的十进制字面量无效：'b'
这个错误表明你尝试将一个字符串（在这个例子中是 'b'）转换为整数，但是这个字符串不是一个有效的整数表示（因为它包含非数字字符）
5、ZeroDivisionError: division by zero  零除错误：除以零
这么多的错误类型 不需要记，看到常见的报错信息能够眼熟 积累的就可以。

定位和排查错误的方法？-- 这个是基本的能力，需要提升。
1、看console 出错的函数，点击文件跳转到具体出错行数。
2、debug调试：断言 + 单步调试 == 重点


语法：
try：
    可能会出现错误的代码
except:
    如果报错了执行这里的代码

代码运行的逻辑： 效果-报错不会影响后续的代码运行。
1、先运行try语句里的代码 【可能会出错的代码】
2、如果出错了，执行except语句里的代码
3、如果没有出错，不会执行except代码 直接往后运行后续的代码

异常捕获了之后，后面的代码依然能正常运行。-- 报错不会影响后续的代码运行。

思考： 什么样代码可能会出错了？--需要判断，加try。
- 1、用户输入数据的时候 --input 【类比功能测试：对用户输入的数据永远进行异常的测试，确保代码有处理异常数据能力】
- 2、数据是函数传参进来 用户传参数
- 3、数据来自于某个函数或者方法的执行结果 【接口测试响应消息--code-200,400,500】，返回值用作其他的地方；
- 4、断言： 预期结果 vs 实际结果，一致的 通过，不一致 失败的。== 断言失败 异常


"""
# 1、NameError: name 'a' is not defined
# print(a)  # 终止代码运行 后面的代码运行不到。
# print(666) # 这行代码不会执行

# 2、IndexError: list index out of range
# a = [1,2,3]
# print(a[4])

# 3、KeyError: 'age'
# a = {"name":"C默"}
# print(a["age"])

#4、ValueError: invalid literal for int() with base 10: 'b'
# a = "b"
# print(int(a))   #访问一个字符串 想给进行数据类型转换，但是"b"不是一个有效的整数。
#
# #5、ZeroDivisionError: division by zero
# print(10/0)

# 异常捕获

# a = [1,2,3]
# try:
#     print(a[5])
# except Exception as e:
#     print({e})
#     raise e

# 异常捕获获取详细的错误信息 显示出来：
# - 主要的一个场景，拿到具体的报错信息，记录到项目日志里方便后续定位和分析问题。
# - except Exception as err ： 万能的捕获 不管是什么类型错误  都可以捕获。
#
# 以上的用法 没针对具体的不同的错误类型 做不同的操作处理：
# - 记录日志
# - 发送警告
# - 发送邮件通知开发人员
# .....
#
# 语法规则：
# 1、如果第一行代码报错了，直接去运行except里的代码  try里面后续的代码不会执行；
# 2、发现except里面的代码错误类型不匹配 也不会捕获到，整个代码报错抛出了，代码终止运行。
#
# 异常捕获的原则：
# 1、优先用特定类型错误捕获进行异常捕获操作。
# 2、最后可以用一个Exception兜底。

from loguru import logger
from tools.handle_logs import logger

# 1、异常捕获 -万能捕获
# a = [1,2,3]
# try:
#     print(a[20])  # 可能会出错的代码，如果没有出错，不会执行except代码 直接往后运行后续的代码
# except Exception as err:  # 捕获异常信息并具体错误信息保存在err变量--日志记录。
#     logger.info(f"列表的索引错误：{err}")
#     raise err   #如果不把问题抛出那么allure 统计的时候把把错误的数据统计为正确的，影响数据准确性。
#
# print("这里的代码运行了么？")

# 2、针对不同的错误进行 捕获
# a = [1,2,3]
# try:
#     print(a[100])   # 出错了 类型是 IndexError
# # except KeyError as err:  #  捕获的错误类型是keyError  不可以捕获到
# except IndexError as err:  # 捕获的错误类型是IndexError  可以捕获到
#     print(f"列表的索引错误：{err}")
# print("这里的代码运行了么？")

# 3、练习
# a = [1,2,3]
# b = {"name":"C默"}
# try:
#     print(a[10])   # 出错了 类型是 IndexError
#     print(b["age"])  # 出错了 类型是 KeyError
# except KeyError as err:  #  捕获的错误类型是keyError  不可以捕获到
#     print(f"字典的索引错误：{err}")
# except IndexError as err:  # 捕获的错误类型是IndexError  可以捕获到
#     print(f"列表的索引错误：{err}")
# except Exception as err:   # 放在最后 进行兜底  前面没有捕获都会被这个万能捕获到。
#     print(f"代码执行错误！{err}")
# print("这里的代码运行了么？")

# 上述代码中捕获了IndexError的异常，没有捕获到KeyError as err。因为IndexError异常捕获后，直接处理了print() 不会继续捕获
# 如果希望两个异常同时被捕获到，需要分开写。ty：  except：

# except 块只捕获了 KeyError 和 IndexError。
# 当你尝试访问列表 a 的第 11 个元素（索引为 10 的位置，因为列表索引从 0 开始）时， 会抛出一个 IndexError，这个异常被你的 except IndexError 块捕获了。
# 但是，当你尝试访问字典 b 中不存在的键 "age" 时，会抛出一个 KeyError，但是这个异常没有被立即捕获，因为 except KeyError 块在 except IndexError 块之后，
# 并且由于 IndexError 已经被捕获并处理，所以代码不会继续执行到 except KeyError 块。

# 然而，你注意到 except KeyError 似乎没有捕捉到 KeyError 异常，也没有报错。这是因为当 IndexError 被捕获并处理后，程序继续执行 try 块之后的代码，即 print("这里的代码运行了么？")，因此你看到了这条输出。
# 由于 KeyError 是在 IndexError 被处理之后才会发生的（如果 IndexError 没有抛出的话），但在这个流程中它实际上没有机会发生，因为 print(a[10]) 语句已经抛出了 IndexError 并被捕获。
#
# 如果你想要同时捕获这两种异常，并且希望看到每种异常被捕获时的输出，你可以将 except 块组合起来，或者使用多个 try-except 结构。但是，由于在你的代码中 print(b["age"]) 是在 print(a[10]) 后面，
# 一旦 IndexError 被抛出，print(b["age"]) 就不会被执行，因此 KeyError 也不会被抛出。
# 如果你想要模拟两种异常都被捕获的情况，你可以将这两个可能导致异常的语句分开到两个 try-except 块中，或者重新组织代码以便两个异常都有可能发生。

# 3、raise 主动抛出
"""
raise： 代码没有报错，主动给他抛出错误。
- 程序员根据业务逻辑 判断这个代码处理某种情况的时候，报错。==不符合业务需求的时候。

为什么要主动抛出使用场景：
1、代码里如果是代码本身语法错误，报错了，这种问题好解决的；
2、隐藏的问题，代码不报错，处理比较麻烦的。通过程序员对于开发/自动化的需求理解，提前想到代码可能会有问题；
 - 主动raise错误，提前更早地发现问题。主动排除代码错误并终止代码运行。
 
因为异常捕获NameError类型不符合，捕获失败，导致报错。影响后续代码。

3、自动化测试：执行测试用例要进行断言的【预期结果 vs 执行结果】
  -- 一致--断言通过，测试用例通过的；
  -- 不一致，断言失败，测试用例执行不通过的 【项目的bug 或者代码的bug】== 异常：AssertionError
    -- 如果失败的用例，我们自动化框架要处理【异常捕获】，记录错误日志，方便定位问题，提价bug或者修复框架bug
    -- 项目测试报告里会记录这条是失败的用例么？不会的，导致测试报告的结果统计不准确的。
      - 所以希望结果： 捕获完成后 记录日志； 然后抛出这个断言失败的错误，方便测试报告结果统计。--raise


# 总结：核心判断标准
# 若异常意味着 “测试目标未达成” → 必须 raise，让用例标记为失败（如接口返回错误、断言不通过）。
# 若异常是 “可容忍的小问题” 或 “已通过其他方式解决” → 无需 raise，避免干扰测试结果。
# 简单说：需要让框架知道的错误就 raise，不需要让框架知道的（已处理或无关紧要）就不 raise。
"""

# 举例： 需求文档： 对函数的参数类型有限制，不能输入负数： 如果输入负数代码抛出异常，终止运行。
# def add(a,b):
#     return a + b
# print(add(1,2))
# print(add(-1,-2))

# 处理业务需求：对函数的参数类型有限制，不能输入负数： 如果输入负数代码抛出异常，终止运行。
# def add(a,b):
#     if a<0 or b<0:
#         raise ValueError("数据不能为负数！")  # 主动抛出异常 raise关键字
#     return a + b
# print(add(1,2))
# print(add(-1,-2)) # 调用函数的 运行到这个行代码 主动报错
#
# # 主动抛出异常，代码报错，后续的代码肯定不会执行了。
# print("running!!") # 不会运行


# def login(username, password):
#     # if type(username) == int and type(password) == int:
#     if isinstance(username,int,) and isinstance(password,int):
#         print(f"登录成功 账号:{username},密码:{password}")
#
#     else:
#         raise ValueError("账号、密码应为整型")
#
# print("runner")
# login("13100001234", 222222)  # 这行会抛出异常，并且程序会在这里停止
# print("这行代码在异常之后，所以不会执行")  # 这行代码不会被执行
#


"""
finally： 兜底的，写在这个里面的代码 不管是否捕获了异常都会执行代码
- 代码看起来更加完整而已，没有特别的意义。 能看懂。

"""
actural = 5
expected = 6
try:
    assert actural == expected,"断言失败，5!=6"
# except AssertionError as err:  #会捕获成功的，不会报错。
except NameError as err:  #  不会捕获成功的，所以会报错。
    print(f"断言失败了{err}")  # 这行记录日志  print代替日志记录
finally:
    print("不管异常是否被捕获了，都会执行的代码！")
    raise

#因为异常捕获NameError类型不符合，捕获失败，导致报错。影响后续代码。